#include "../Header/Movable.h"
#include "../Header/GameObject.h"
#include "../Header/Vec2.h"
#include "../Header/Game.h"
#include <math.h>

int ClientHeight = 512;
int ClientWidth = 512;

using namespace std;

Movable::Movable(GameObject * o)
{
	ID = "Movable";
	parent = o;
	objects = parent->getParent()->getObjects();
}

Vec2 Movable::Collide(Vec2 vec, GameObject * o2)						/*This method returns the altered potential 
																		position vec, given it has already collided
																		with o2*/
{
	GameObject * o1 = parent;											/*Set o1 to the object that this component
																		belongs to*/

	int x1 = vec.x + o1->getPosition().x + o1->getDimension().x/2;		/*Set x1 to the sum of the tentative movement
																		vector's x component, the current pos
																		of this object, and half the x dimension of
																		this object*/
	int x2 = o2->getPosition().x + o2->getDimension().x/2;				/*Set x2 to the sum of the current x position
																		of the second object and half the second object's
																		x dimension*/
	int y1 = vec.y + o1->getPosition().y + o1->getDimension().y/2;		/*Set y1 to the sum of the new tentative
																		position's x component, the current pos
																		of this object, and half the x dimension
																		of this object*/
	int y2 = o2->getPosition().y + o2->getDimension().y/2;				/*Set y2 to the sum of the second object's
																		position's y component and half the second
																		object's y dimension*/

	int r1 = max(o1->getDimension().x, o1->getDimension().y)/4;			/*Set r1 to half greater of the two dimensions*/
	int r2 = max(o2->getDimension().x, o2->getDimension().y)/4;			/*Set r2 to half the greater of the two
																		dimensions of the second object*/

	int a = x1 - x2;													/*Set some random variable a to the x distance*/
	int b = y1 - y2;													/*Set some random variable b to the y distance*/

	double angle = atan2((double)b,(double)a);								/*Find the angle formed by the line between the
																		centers of the two objects*/
	Movable * c1 = static_cast<Movable*>(o1->getComponent("Movable"));	/*Set c1 to the Movable component of this obj*/
	Movable * c2 = static_cast<Movable*>(o2->getComponent("Movable"));	/*Set c2 tot he Movable component of the second
																		object*/

	bool m1 = (c1 != NULL);												/*make sure that this object can move*/
	bool m2 = (c2 != NULL);												/*make sure the second obj can move*/

	double dist = sqrt((double) pow((double) a, 2) + pow((double)b, 2));/*set the distance between the two objects
																		 via the distance form*/

	double diff = abs((double) dist - (r1 + r2))/2;						/*set half the disparity between the actual
																		dist and what it is to "diff"*/

	double moveAmount=0;												/*create some value moveAmount*/
	
	if(m1 && m2)														/*If both objects involved can move*/
	{
		moveAmount = diff;												/*Set the moveAmount to half the total
																		disparity*/
		Vec2 v1;														/*Create some vector v1*/
		Vec2 v2;														/*Create some vector v2*/
		v1.x = moveAmount * cos(angle);									/*Set the x component of v1 to the x
																		component of the moveAmount*/
		v2.x = -moveAmount * cos(angle);								/*Set the x component of v2 to the x
																		component of the negative moveAmount*/
		v1.y = moveAmount * sin(angle);									/*Set the y component of v1 to the y
																		component of the moveAmount*/
		v2.y = -moveAmount * sin(angle);								/*Set the y component of v2 to the y
																		component of the negative moveAmount*/

		v1.x = v1.x + vec.x;											/*Change the x component of the output vector
																		to the sum of the original intent and the
																		amount necessary to not break collision*/
		v1.y = v1.y + vec.y;											/*Change the y component of the output vector
																		to the sum of the original intent and the
																		amount necessary to not break collision*/
		v2.x = v2.x + vec.x;											/*Change the x component of the output vector
																		to the sum of the original intent and the
																		amount necessary to not break collision*/
		v2.y = v2.y + vec.y;											/*Change the y component of the output vector
																		to the sum of the original intent and the
																		amount necessary to not break collision*/

		c2->Move(v2);													/*Call "Move" on the second object by
																		the amount already determined*/

		return(v1);														/*Return the new vector that has been
																		edited*/

	}
	else if(m1 || m2)													/*If only one of the objects can move*/
	{
		moveAmount = (int)(diff * 2);									/**/
		Vec2 v;															/**/
		if(o1->isSolid() && o2->isSolid())
		{
			v.x = (int)(moveAmount * cos(angle));						/**/
			v.y = (int)(moveAmount * cos(angle));						/**/
		}
		else
		{
			v.x = 0;
			v.y = 0;
		}

		if(m1) return v;												/**/
	}
}

Vec2 Movable::CheckCollision(Vec2 vec)								/*This takes the argument of the movement
																	vector and returns the actual position
																	the object should be based on a collision
																	algorithm*/
{
	GameObject * o = parent;
	vector<GameObject*>::iterator it = objects.begin();
	double r1 = max(o->getDimension().x, o->getDimension().y)/2;	/*Radius of circle 1 is the x or y 
																	dimension of this object divided by
																	2, whichever is greater*/

	int x1 = vec.x + o->getPosition().x + o->getDimension().x/2;	/*Set the x center of the new pos, "x1," to
																	the sum of the x componenet of the mov vec,
																	the old x position, and half the x dimesion
																	of the object*/

	int y1 = vec.y + o->getPosition().y + o->getDimension().y/2;	/*Set the y center of the new pos, "y1" to
																	the sum of y component of the movement vec,
																	the old y position, and half the y dimesion
																	of the object*/
		
	double r2=0;													/*Create a radius for the second circle*/
	int x2=0;														/*Create x center of the second obj,"x2"*/
	int y2=0;														/*Create y center of the second obj, "y2"*/
	for(it; it < objects.end(); ++it){								/*Iterate through every object in the game*/
		if((*it)->isSolid() && (*it) != parent)						/*If that object is solid*/
		{
			r2 = max((*it)->getDimension().x, (*it)->getDimension().y)/2;
																	/*Radius of circle 2 is the x or y dimension
																	of the currently examined object divided by 2,
																	whichever is greater*/
			x2 = (*it)->getPosition().x + (*it)->getDimension().x/2;/*Set the center of the second obj to the sum of the
																	current x position of the object and one half
																	the x dimension*/
			y2 = (*it)->getPosition().y + (*it)->getDimension().y/2;/*Set the center of the second obj to the sum of the
																	current y position of the object and one half
																	the y dimension*/

			int a = pow((double)x1 - x2, 2);						/*set some mysterious variable a to the square
																	of the x difference*/
			int b = pow((double)y1 - y2, 2);						/*set some myserious variable b to the square
																	of the y difference*/

			double dist = sqrt((double)a + b);						/*set the dist to the squareroot of the sum
																	of a and b*/

			if(dist+3 < r1 + r2)										/*if that distance is less than the sum of the
																	radii*/
			{
				return Collide(vec, (*it));							/*Call collide on the potential position of this
																	object and the object this one is colliding with
																	and return the value returned by collide*/
			}
		}
	}
	return(vec);													/*Otherwise, return the movement vector
																	unaltered*/
}

void Movable::Move(Vec2 vec)				/*this method takes a movement vector to
											change the object's pos by*/
{
	Vec2 pos = parent->getPosition();		//set a temporary vector pos as the position of the object courtesy of the GameObject's method:getPosition()
	if(parent->isSolid())
	{
		pos = CheckCollision(vec);				//call check collision on the new position
	}

	Vec2 tempParentPos;
	tempParentPos.x = parent->getPosition().x + pos.x;
	tempParentPos.y = parent->getPosition().y + pos.y;


	Vec2 extraMov;
	extraMov.x = 0;
	extraMov.y = 0;
	
	bool acceptablePos = false;

	while(!acceptablePos)
	{
		acceptablePos = true;
		if(tempParentPos.x  - parent->getDimension().x< -ClientWidth/2)
		{
			extraMov.x++;
			acceptablePos = false;
		}
		if(tempParentPos.x + parent->getDimension().x> ClientWidth/2)
		{
			extraMov.x--;
			acceptablePos = false;
		}
		if(tempParentPos.y - parent->getDimension().y < -ClientHeight/2)
		{
			extraMov.y++;
			acceptablePos = false;
		}
		if(tempParentPos.y + parent->getDimension().y > ClientHeight/2)
		{
			extraMov.y--;
			acceptablePos = false;
		}
		tempParentPos.x = tempParentPos.x + extraMov.x;
		tempParentPos.y = tempParentPos.y + extraMov.y;
	}

	pos.x = pos.x + extraMov.x;
	pos.y = pos.y + extraMov.y;

	parent->addPosition(pos);				//alter the Object's position by the result of the checkCollision

}